#include <QCommandLineParser>
#include <QCoreApplication>
#include <QMetaEnum>
#include <QObject>
#include <QTextStream>

static QTextStream qout(stdout);
static QTextStream qerr(stderr);
inline QTextStream &qendl(QTextStream &s) { return Qt::endl(s); }

#include <QtDBus/QDBusConnection>
#include <QtDBus/QDBusConnectionInterface>

static const char *serviceName = "com.deepin.daemon.Keybinding";
static const char *objectPath = "/com/deepin/daemon/Keybinding";
static const char *interfaceName = "com.deepin.daemon.Keybinding";

#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>

#include "helper.h"
#include <QTranslator>

enum ErrorInfo : int {
  Success,
  NotSupport,
  ConnectFailed,
  InvalidOption,
  NoKeywordInput,
  ArgumentNeedMore
};

bool checkSupported() {
  return QDBusConnection::sessionBus().interface()->isServiceRegistered(
      QString::fromUtf8(serviceName));
}

template <typename T> const T processReply(const QDBusPendingReply<> &reply) {
  const QDBusPendingReply<T> r = reply;
  return r.value();
};

const QDBusPendingReply<>
kbrun(const QString &cmd, const QList<QVariant> args = QList<QVariant>()) {
  QDBusMessage message = QDBusMessage::createMethodCall(serviceName, objectPath,
                                                        interfaceName, cmd);
  message.setArguments(args);
  return QDBusConnection::sessionBus().call(message);
}

// if its type is NoError, then return true
bool errorHandle(const QDBusError &derr) {
  if (derr.type() == QDBusError::NoError) {
    return true;
  }
  qerr << QObject::tr("[%1] %2").arg(derr.name(), derr.message()) << qendl;
  return false;
}

void printKeyBinding(const QJsonObject &value, bool human = true,
                     Helper::InfoShows displays = Helper::All,
                     Helper::Filters filter = Helper::F_All) {
  auto id = value.value("Id").toString();
  auto t = value.value("Type").toInt();
  auto type = QMetaEnum::fromType<Helper::InfoType>().key(t);
  auto accels = value.value("Accels").toVariant().toStringList();
  auto name = value.value("Name").toString();
  auto printable =
      (filter.testFlag(Helper::F_System) && t == Helper::System) ||
      (filter.testFlag(Helper::F_Custom) && t == Helper::Custom) ||
      (filter.testFlag(Helper::F_Media) && t == Helper::Media) ||
      (filter.testFlag(Helper::F_Window) && t == Helper::Window) ||
      (filter.testFlag(Helper::F_Workspace) && t == Helper::Workspace);
  if (printable) {
    if (human) {
      qout << QString(15, '-') << qendl;
      if (displays.testFlag(Helper::ID)) {
        qout << QObject::tr("ID: ") << id << qendl;
      }
      if (displays.testFlag(Helper::Type)) {
        qout << QObject::tr("Type: ") << QObject::tr(type) << qendl;
      }
      if (displays.testFlag(Helper::Shortcut)) {
        qout << QObject::tr("Accels: ") << accels.join(";") << qendl;
      }
      if (displays.testFlag(Helper::Name)) {
        qout << QObject::tr("Name: ") << name << qendl;
      }
      if (displays.testFlag(Helper::Exec)) {
        auto exec = value.value("Exec");
        if (!exec.isUndefined()) {
          qout << QObject::tr("Exec: ") << exec.toString() << qendl;
        }
      }
    } else {
      auto exec = value.value("Exec");
      QStringList contents;
      if (displays.testFlag(Helper::ID)) {
        contents.append(id);
      }
      if (displays.testFlag(Helper::Type)) {
        contents.append(type);
      }
      if (displays.testFlag(Helper::Shortcut)) {
        contents.append(accels.join(','));
      }
      if (displays.testFlag(Helper::Name)) {
        contents.append(name);
      }
      if (displays.testFlag(Helper::Exec)) {
        if (!exec.isUndefined()) {
          contents << exec.toString();
        }
      }
      qout << contents.join('\t') << qendl;
    }
  }
}

void printKeyBindings(const QString &json, bool human = true,
                      Helper::InfoShows displays = Helper::All,
                      Helper::Filters filter = Helper::F_All) {
  auto jsdoc = QJsonDocument::fromJson(json.toUtf8());
  if (jsdoc.isArray()) {
    auto arr = jsdoc.array();
    for (auto it = arr.begin(); it != arr.end(); it++) {
      printKeyBinding(it->toObject(), human, displays, filter);
    }
  } else if (jsdoc.isObject()) {
    printKeyBinding(jsdoc.object(), human, displays, filter);
  }
}

void installLang() {
  auto locale = QLocale::system();
  auto langPath = ":/dkb/lang";
  if (locale.language() == QLocale::Chinese) {
    auto trans = new QTranslator(qApp);
    trans->load("qt_zh_CN.qm", langPath);
    qApp->installTranslator(trans);

    trans = new QTranslator(qApp);
    trans->load("qtbase_zh_CN.qm", langPath);
    qApp->installTranslator(trans);

    trans = new QTranslator(qApp);
    trans->load("dkeybindings_zh_CN.qm", langPath);
    qApp->installTranslator(trans);
  } else {
    auto trans = new QTranslator(qApp);
    trans->load("qt_en.qm", langPath);
    qApp->installTranslator(trans);

    trans = new QTranslator(qApp);
    trans->load("qtbase_en.qm", langPath);
    qApp->installTranslator(trans);

    trans = new QTranslator(qApp);
    trans->load("dkeybindings_en.qm", langPath);
    qApp->installTranslator(trans);
  }
}

int main(int argc, char *argv[]) {
  QCoreApplication a(argc, argv);

  installLang();

  if (!checkSupported()) {
    qerr << QObject::tr("[Error] com.deepin.daemon.Keybinding doesn't exist")
         << qendl;
    return NotSupport;
  }

  if (!QDBusConnection::sessionBus().isConnected()) {
    qerr << "Cannot connect to the D-Bus sessionBus" << qendl;
    return ConnectFailed;
  }

  a.setApplicationVersion("1.0.0");

  QCommandLineParser parser;
  parser.setApplicationDescription(QObject::tr(
      "A Keybindings manager tool for Deepin, written by wingsummer."));
  parser.addHelpOption();
  parser.addVersionOption();
  parser.setSingleDashWordOptionMode(
      QCommandLineParser::SingleDashWordOptionMode::ParseAsLongOptions);
  parser.setOptionsAfterPositionalArgumentsMode(
      QCommandLineParser::OptionsAfterPositionalArgumentsMode::ParseAsOptions);

  QList<QCommandLineOption> ops;
  QCommandLineOption addOp(QStringList{"add", "a"},
                           QObject::tr("add a custom keybinding in Deepin."),
                           QObject::tr("ID"));
  QCommandLineOption rmOp(QStringList{"remove", "del", "delete", "rm"},
                          QObject::tr("remove custom keybindings in Deepin."),
                          QObject::tr("ID"));
  QCommandLineOption queryOp(
      QStringList{"query", "q"},
      QObject::tr("query the keybinding infomation by ID."),
      QObject::tr("keyword"));
  QCommandLineOption searchOp(
      QStringList{"search", "find", "s"},
      QObject::tr("search the keybinding infomation by ID."
                  "The keyword ID can use the regex or the part of it."),
      QObject::tr("keyword"));
  QCommandLineOption modOp(QStringList{"mod", "m"},
                           QObject::tr("Modify keybinding in Deepin."),
                           QObject::tr("ID"));
  QCommandLineOption listOp(QStringList{"list", "l"},
                            QObject::tr("List all keybindings in Deepin."));
  QCommandLineOption disableOp(
      QStringList{"disable", "d"},
      QObject::tr("Disable keybindings in Deepin."
                  "It will clear their accelerator keys."),
      QObject::tr("ID"));
  QCommandLineOption resetOp(QStringList{"reset", "r"},
                             QObject::tr("Reset keybindings in Deepin."));
  QCommandLineOption print0Op(
      QStringList{"print0", "0", "null"},
      QObject::tr("Make the output more convenient for other programs to use"
                  "(format: <id>\t<type>\t<accels>\t<name>\t<exec>)."));
  QCommandLineOption typeOp(
      QStringList{"type", "t"},
      QObject::tr("Specific the operation type of keybindings in Deepin."
                  "There are some values you can use:\n"
                  "* 0(or s for short): System type \n"
                  "* 1(or c for short): Custom type\n"
                  "* 2(or m for short): Media type\n"
                  "* 3(or win for short): Window type\n"
                  "* 4(or ws for short): Workspace type\n"
                  "* 5(or a for short): All types(default value)\n"
                  "This option also can be used to query and modify the "
                  "keybinding, "
                  "however, the value 5/a is not valid."),
      QObject::tr("type"), "a");
  QCommandLineOption execOp(
      QStringList{"exec", "x"},
      QObject::tr("The exec property option for adding or "
                  "modifying the keybinding in Deepin."),
      QObject::tr("Action"));
  QCommandLineOption idOp(
      "id",
      QObject::tr(
          "The id property option for querying the keybinding in Deepin."),
      QObject::tr("ID"));
  QCommandLineOption accelOp(
      QStringList{"accel", "sc"},
      QObject::tr("The accel property option for adding, "
                  "querying or modifying the keybinding in Deepin."),
      QObject::tr("Accels"));
  QCommandLineOption nameOp(
      QStringList{"name", "n"},
      QObject::tr("The name property option for modifying the keybinding "
                  "in Deepin."),
      QObject::tr("Name"));
  QCommandLineOption checkOp(
      QStringList{"check", "c"},
      QObject::tr("Check the shortcut is already occupied."
                  "If avaliable, it will output 1. "
                  "Otherwise, it will output 0 with a new line following and "
                  "show the info of keybinding occupied."),
      QObject::tr("shortcut"));

  QCommandLineOption withIDOp(QStringList{"with-id", "WI"},
                              QObject::tr("make output keybindings with ID"));
  QCommandLineOption withNameOp(
      QStringList{"with-name", "WN"},
      QObject::tr("make output keybindings with name"));
  QCommandLineOption withAccelOp(
      QStringList{"with-accel", "WA"},
      QObject::tr("make output keybindings with accelerator keys"));
  QCommandLineOption withExecOp(
      QStringList{"with-exec", "WE"},
      QObject::tr("make output keybindings with execution command"));
  QCommandLineOption withTypeOp(
      QStringList{"with-type", "WT"},
      QObject::tr("make output keybindings with type"));

  ops << addOp << rmOp << modOp << listOp << searchOp << queryOp << disableOp
      << resetOp << typeOp << print0Op << execOp << idOp << accelOp << nameOp
      << checkOp << withIDOp << withExecOp << withNameOp << withTypeOp
      << withAccelOp;
  parser.addOptions(ops);

  parser.process(a);

  if (!parser.isSet(listOp) && !parser.isSet(addOp) && !parser.isSet(rmOp) &&
      !parser.isSet(modOp) && !parser.isSet(disableOp) &&
      !parser.isSet(resetOp) && !parser.isSet(searchOp) &&
      !parser.isSet(searchOp) && !parser.isSet(checkOp)) {
    parser.showHelp(Success);
  } else if (parser.isSet(listOp) && !parser.isSet(addOp) &&
             !parser.isSet(rmOp) && !parser.isSet(modOp) &&
             !parser.isSet(disableOp) && !parser.isSet(resetOp) &&
             !parser.isSet(searchOp) && !parser.isSet(searchOp) &&
             !parser.isSet(checkOp)) {
    // list function
    auto o = parser.value(typeOp);
    QDBusPendingReply<> res;
    if (o == "0" || o == "s") {
      res = kbrun("ListShortcutsByType", {Helper::System});
    } else if (o == "1" || o == "c") {
      res = kbrun("ListShortcutsByType", {Helper::Custom});
    } else if (o == "2" || o == "m") {
      res = kbrun("ListShortcutsByType", {Helper::Media});
    } else if (o == "3" || o == "win") {
      res = kbrun("ListShortcutsByType", {Helper::Window});
    } else if (o == "4" || o == "ws") {
      res = kbrun("ListShortcutsByType", {Helper::Workspace});
    } else if (o == "5" || o == "a") {
      res = kbrun("ListAllShortcuts");
    } else {
      qerr << QObject::tr("[Error] Invalid type option") << qendl;
      return InvalidOption;
    }

    Helper::InfoShows show = Helper::InfoShow::None;
    if (parser.isSet(withIDOp)) {
      show.setFlag(Helper::ID);
    }
    if (parser.isSet(withTypeOp)) {
      show.setFlag(Helper::Type);
    }
    if (parser.isSet(withNameOp)) {
      show.setFlag(Helper::Name);
    }
    if (parser.isSet(withAccelOp)) {
      show.setFlag(Helper::Shortcut);
    }
    if (parser.isSet(withExecOp)) {
      show.setFlag(Helper::Exec);
    }
    if (show == Helper::None) {
      show = Helper::All;
    }

    if (errorHandle(res.error())) {
      printKeyBindings(processReply<QString>(res), !parser.isSet(print0Op),
                       show);
    }
  } else if (!parser.isSet(listOp) && !parser.isSet(addOp) &&
             !parser.isSet(rmOp) && !parser.isSet(modOp) &&
             !parser.isSet(disableOp) && !parser.isSet(resetOp) &&
             parser.isSet(searchOp) && !parser.isSet(queryOp) &&
             !parser.isSet(checkOp)) {
    // search function
    auto keyword = parser.value(searchOp).trimmed();
    if (keyword.isEmpty()) {
      qerr << QObject::tr("[Error] Please input keyword to search") << qendl;
      return NoKeywordInput;
    }
    Helper::Filter filter = Helper::F_All;
    if (parser.isSet(typeOp)) {
      auto o = parser.value(typeOp);
      if (o == "0" || o == "s") {
        filter = Helper::F_System;
      } else if (o == "1" || o == "c") {
        filter = Helper::F_Custom;
      } else if (o == "2" || o == "m") {
        filter = Helper::F_Media;
      } else if (o == "3" || o == "win") {
        filter = Helper::F_Window;
      } else if (o == "4" || o == "ws") {
        filter = Helper::F_Workspace;
      } else if (o == "5" || o == "a") {
        filter = Helper::F_All;
      } else {
        qerr << QObject::tr("[Error] Invalid type option") << qendl;
        return InvalidOption;
      }
    }
    auto res = kbrun("SearchShortcuts", {keyword});

    Helper::InfoShows show = Helper::InfoShow::None;
    if (parser.isSet(withIDOp)) {
      show.setFlag(Helper::ID);
    }
    if (parser.isSet(withTypeOp)) {
      show.setFlag(Helper::Type);
    }
    if (parser.isSet(withNameOp)) {
      show.setFlag(Helper::Name);
    }
    if (parser.isSet(withAccelOp)) {
      show.setFlag(Helper::Shortcut);
    }
    if (parser.isSet(withExecOp)) {
      show.setFlag(Helper::Exec);
    }
    if (show == Helper::None) {
      show = Helper::All;
    }

    if (errorHandle(res.error())) {
      printKeyBindings(processReply<QString>(res), !parser.isSet(print0Op),
                       show, filter);
    }
  } else if (!parser.isSet(listOp) && !parser.isSet(addOp) &&
             !parser.isSet(rmOp) && !parser.isSet(modOp) &&
             !parser.isSet(disableOp) && !parser.isSet(resetOp) &&
             !parser.isSet(searchOp) && parser.isSet(queryOp) &&
             !parser.isSet(checkOp)) {
    // query function
    auto o = parser.value(typeOp);
    auto id = parser.value(queryOp);
    QDBusPendingReply<> res;

    Helper::InfoShows show = Helper::InfoShow::None;
    if (parser.isSet(withIDOp)) {
      show.setFlag(Helper::ID);
    }
    if (parser.isSet(withTypeOp)) {
      show.setFlag(Helper::Type);
    }
    if (parser.isSet(withNameOp)) {
      show.setFlag(Helper::Name);
    }
    if (parser.isSet(withAccelOp)) {
      show.setFlag(Helper::Shortcut);
    }
    if (parser.isSet(withExecOp)) {
      show.setFlag(Helper::Exec);
    }
    if (show == Helper::None) {
      show = Helper::All;
    }

    if (o == "0" || o == "s") {
      res = kbrun("Query", {id, Helper::System});
      if (errorHandle(res.error())) {
        printKeyBindings(processReply<QString>(res), !parser.isSet(print0Op),
                         show);
      }
    } else if (o == "1" || o == "c") {
      res = kbrun("Query", {id, Helper::Custom});
      if (errorHandle(res.error())) {
        printKeyBindings(processReply<QString>(res), !parser.isSet(print0Op),
                         show);
      }
    } else if (o == "2" || o == "m") {
      res = kbrun("Query", {id, Helper::Media});
      if (errorHandle(res.error())) {
        printKeyBindings(processReply<QString>(res), !parser.isSet(print0Op),
                         show);
      }
    } else if (o == "3" || o == "win") {
      res = kbrun("Query", {id, Helper::Window});
      if (errorHandle(res.error())) {
        printKeyBindings(processReply<QString>(res), !parser.isSet(print0Op),
                         show);
      }
    } else if (o == "4" || o == "ws") {
      res = kbrun("Query", {id, Helper::Workspace});
      if (errorHandle(res.error())) {
        printKeyBindings(processReply<QString>(res), !parser.isSet(print0Op),
                         show);
      }
    } else if (o == "5" || o == "a") {
      for (int i = 0; i < Helper::InfoType_MAX_COUNT; i++) {
        auto res = kbrun("Query", {id, i});
        switch (res.error().type()) {
        case QDBusError::NoError:
          printKeyBindings(processReply<QString>(res), !parser.isSet(print0Op),
                           show);
          break;
        case QDBusError::Other:
          break;
        default:
          errorHandle(res.error());
          break;
        }
      }
    } else {
      qerr << QObject::tr("[Error] Invalid type option") << qendl;
      return InvalidOption;
    }
  } else if (!parser.isSet(listOp) && parser.isSet(addOp) &&
             !parser.isSet(rmOp) && !parser.isSet(modOp) &&
             !parser.isSet(disableOp) && !parser.isSet(resetOp) &&
             !parser.isSet(searchOp) && !parser.isSet(queryOp) &&
             !parser.isSet(checkOp)) {
    // add function
    if (!parser.isSet(execOp) || !parser.isSet(accelOp)) {
      qerr << QObject::tr(
                  "[Error] Please give enough params to add a keybinding")
           << qendl;
      return ArgumentNeedMore;
    }
    if (parser.isSet(typeOp)) {
      qerr << QObject::tr("[Warning] the TYPE option is ignored")
           << QObject::tr("(You can only add custom keybindings)") << qendl;
    }
    auto exec = parser.value(execOp);
    auto name = parser.value(addOp);
    auto accel = parser.value(accelOp);
    QDBusPendingReply<QString, int> res =
        kbrun("AddCustomShortcut", {name, exec, accel});
    auto ret = res.value();
    if (errorHandle(res.error())) {
      qout << res.argumentAt(0).toString() << '\t'
           << res.argumentAt(1).toString() << qendl;
    }
  } else if (!parser.isSet(listOp) && !parser.isSet(addOp) &&
             parser.isSet(rmOp) && !parser.isSet(modOp) &&
             !parser.isSet(disableOp) && !parser.isSet(resetOp) &&
             !parser.isSet(searchOp) && !parser.isSet(queryOp) &&
             !parser.isSet(checkOp)) {
    // remove function
    if (parser.isSet(typeOp)) {
      auto o = parser.value(typeOp);
      auto ids = parser.values(rmOp);
      if (ids.isEmpty()) {
        qerr << QObject::tr("[Error] Please input any keyword to remove")
             << qendl;
        return NoKeywordInput;
      }
      QDBusPendingReply<> res;
      if (o == "0" || o == "s") {
        for (auto &id : ids) {
          errorHandle(kbrun("Delete", {id, Helper::System}).error());
        }
      } else if (o == "1" || o == "c") {
        for (auto &id : ids) {
          errorHandle(kbrun("Delete", {id, Helper::Custom}).error());
        }
      } else if (o == "2" || o == "m") {
        for (auto &id : ids) {
          errorHandle(kbrun("Delete", {id, Helper::Media}).error());
        }
      } else if (o == "3" || o == "win") {
        for (auto &id : ids) {
          errorHandle(kbrun("Delete", {id, Helper::Window}).error());
        }
      } else if (o == "4" || o == "ws") {
        for (auto &id : ids) {
          errorHandle(kbrun("Delete", {id, Helper::Workspace}).error());
        }
      } else {
        qerr << QObject::tr("[Error] Invalid type option") << qendl;
        return InvalidOption;
      }
    } else {
      auto ids = parser.values(rmOp);
      if (ids.isEmpty()) {
        qerr << QObject::tr("[Error] Please input any keyword to remove")
             << qendl;
        return NoKeywordInput;
      }
      for (auto &id : ids) {
        errorHandle(kbrun("DeleteCustomShortcut", {id}).error());
      }
    }
  } else if (!parser.isSet(listOp) && !parser.isSet(addOp) &&
             !parser.isSet(rmOp) && parser.isSet(modOp) &&
             !parser.isSet(disableOp) && !parser.isSet(resetOp) &&
             !parser.isSet(searchOp) && !parser.isSet(queryOp) &&
             !parser.isSet(checkOp)) {
    // modify function
    if (parser.isSet(typeOp)) {
      qerr << QObject::tr("[Warning] the TYPE option is ignored")
           << QObject::tr("(You can only modify custom keybindings)") << qendl;
    }
    auto id = parser.value(modOp);
    auto res = kbrun("GetShortcut", {id, Helper::Custom});
    if (errorHandle(res.error())) {
      auto value =
          QJsonDocument::fromJson(processReply<QString>(res).toUtf8()).object();
      auto accels = value.value("Accels").toVariant().toStringList();
      QString accel;
      if (!accels.isEmpty()) {
        accel = accels.first();
      }
      auto name = value.value("Name").toString();
      auto exec = value.value("Exec").toString();
      if (parser.isSet(execOp)) {
        exec = parser.value(execOp);
      }
      if (parser.isSet(accelOp)) {
        accel = parser.value(accelOp);
      }
      if (parser.isSet(nameOp)) {
        name = parser.value(nameOp);
      }
      errorHandle(
          kbrun("ModifyCustomShortcut", {id, name, exec, accel}).error());
    }
  } else if (!parser.isSet(listOp) && !parser.isSet(addOp) &&
             !parser.isSet(rmOp) && !parser.isSet(modOp) &&
             parser.isSet(disableOp) && !parser.isSet(resetOp) &&
             !parser.isSet(searchOp) && !parser.isSet(queryOp) &&
             !parser.isSet(checkOp)) {
    // disable function
    if (parser.isSet(typeOp)) {
      auto o = parser.value(typeOp);
      auto ids = parser.values(disableOp);
      if (ids.isEmpty()) {
        qerr << QObject::tr("[Error] Please input any keyword to disable")
             << qendl;
        return NoKeywordInput;
      }
      QDBusPendingReply<> res;
      if (o == "0" || o == "s") {
        for (auto &id : ids) {
          errorHandle(kbrun("Disable", {id, 0}).error());
        }
      } else if (o == "1" || o == "c") {
        for (auto &id : ids) {
          errorHandle(kbrun("Disable", {id, 1}).error());
        }
      } else if (o == "2" || o == "m") {
        for (auto &id : ids) {
          errorHandle(kbrun("Disable", {id, 2}).error());
        }
      } else if (o == "3" || o == "win") {
        for (auto &id : ids) {
          errorHandle(kbrun("Disable", {id, 3}).error());
        }
      } else if (o == "4" || o == "ws") {
        for (auto &id : ids) {
          errorHandle(kbrun("Disable", {id, 4}).error());
        }
      } else {
        qerr << QObject::tr("[Error] Invalid type option") << qendl;
        return InvalidOption;
      }
    } else {
      auto ids = parser.values(rmOp);
      if (ids.isEmpty()) {
        qerr << QObject::tr("[Error] Please input any keyword to disable")
             << qendl;
        return NoKeywordInput;
      }
      for (auto &id : ids) {
        qout << id << qendl;
        errorHandle(kbrun("Disable", {id, 1}).error());
      }
    }
  } else if (!parser.isSet(listOp) && !parser.isSet(addOp) &&
             !parser.isSet(rmOp) && !parser.isSet(modOp) &&
             !parser.isSet(disableOp) && parser.isSet(resetOp) &&
             !parser.isSet(searchOp) && !parser.isSet(queryOp) &&
             !parser.isSet(checkOp)) {
    // reset fuction
    errorHandle(kbrun("Reset").error());
  } else if (!parser.isSet(listOp) && !parser.isSet(addOp) &&
             !parser.isSet(rmOp) && !parser.isSet(modOp) &&
             !parser.isSet(disableOp) && !parser.isSet(resetOp) &&
             !parser.isSet(searchOp) && !parser.isSet(queryOp) &&
             parser.isSet(checkOp)) {
    // check fuction
    auto sc = parser.value(checkOp);
    QDBusPendingReply<bool, QString> res = kbrun("CheckAvaliable", {sc});
    if (errorHandle(res.error())) {
      if (res.argumentAt(0).toBool()) {
        qout << 1 << qendl;
      } else {
        qout << 0 << qendl;
        printKeyBindings(res.argumentAt(1).toString(), !parser.isSet(print0Op));
      }
    }
  } else {
    qerr << QObject::tr("[Error] Invalid option") << qendl;
    return InvalidOption;
  }

  return Success;
}
